package com.xiyuan.smartutils;

import com.xiyuan.smartutils.constants.CodeConstants;
import com.xiyuan.smartutils.constants.EnumConstants;
import com.xiyuan.smartutils.enums.LetterCase;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Bit/Byte组装类 BigEndian 存储时高位在前，低位在后，可读性高，优先知道该值的正负情况，以网络传输、IBM、Motorola CPU为主
 * LittleEndian 存储时低位在前，高位在后，利于对值进行计算，如加减等操作时位运算次数更少，以Intel、DEC CPU为主
 * <p>
 * 有值0x12345678，使用大小端在内存中地址位置 BigEndian LittleEndian byte[0] 0x0000 0x12 0x78
 * byte[1] 0x0001 0x34 0x56 byte[2] 0x0002 0x56 0x34 byte[3] 0x0003 0x78 0x12
 * <p>
 * BigEndian 0x12345678 = 0x00000078 + 0x00005600 + 0x00340000 + 0x12000000 =
 * byte[3]<<0 + byte[2]<<8 + byte[1]<<16 + byte[0]<<24
 * <p>
 * LitEndian 0x12345678 = 0x00000078 + 0x00005600 + 0x00340000 + 0x12000000 =
 * byte[0]<<0 + byte[1]<<8 + byte[2]<<16 + byte[3]<<24
 * <p>
 * 由于Java中byte取值为-127 - 128，因此在作位运算前需转化为0-256 即byte[?] & 0xFF
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */

public final class Bytes implements CodeConstants, EnumConstants {

    public static final Bytes BU = new Bytes(true, _UTF_8_);
    public static final Bytes BG = new Bytes(true, _GBK_);
    public static final Bytes BI = new Bytes(true, _ISO_8859_1_);
    public static final Bytes LU = new Bytes(false, _UTF_8_);
    public static final Bytes LG = new Bytes(false, _GBK_);
    public static final Bytes LI = new Bytes(false, _ISO_8859_1_);
    
    private Charset encoding = _UTF_8_C_;
    private boolean isBigEndian = true;
    
    public Bytes() {
    }
    
    public Bytes(boolean isBigEndian) {
        this.isBigEndian = isBigEndian;
    }
    
    public Bytes(String encoding) {
        this.encoding = Charset.forName(encoding);
    }
    
    public Bytes(boolean isBigEndian, String encoding) {
        this.isBigEndian = isBigEndian;
        this.encoding = Charset.forName(encoding);
    }
    
    /**********************************************************/
    // 以下方法为在给定的偏移位,从指定的字节数组获取数据的方法
    /**********************************************************/
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个 <b>双字节</b> 的char<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return char     得到双字节的char
     */
    public char getChar2(byte[] b, AtomicInteger off) {
        return getChar2(b, off.getAndAdd(2));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个short<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return short    得到short
     */
    public short getShort(byte[] b, AtomicInteger off) {
        return getShort(b, off.getAndAdd(2));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个unsigned short<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return int      得到int
     */
    public int getShortUnsigned(byte[] b, AtomicInteger off) {
        return getShortUnsigned(b, off.getAndAdd(2));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个int<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return int      得到int
     */
    public int getInt(byte[] b, AtomicInteger off) {
        return getInt(b, off.getAndAdd(4));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个unsigned int<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return long     得到long
     */
    public long getIntUnsigned(byte[] b, AtomicInteger off) {
        return getIntUnsigned(b, off.getAndAdd(4));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个float<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个float
     */
    public float getFloat(byte[] b, AtomicInteger off) {
        return getFloat(b, off.getAndAdd(4));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个long<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个long
     */
    public long getLong(byte[] b, AtomicInteger off) {
        return getLong(b, off.getAndAdd(8));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个double<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个double
     */
    public double getDouble(byte[] b, AtomicInteger off) {
        return getDouble(b, off.getAndAdd(8));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读取UTF-8编码字符串
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个String
     */
    public String getString(byte[] b, AtomicInteger off) {
        int offset = off.get();
        int len = b.length - offset;
        if (len < 0) {return null;}
        
        off.getAndAdd(len);
        return new String(b, offset, len, encoding);
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个到结束符为value的String<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 结束符
     * @return 返回一个String
     */
    public String getString(byte[] b, AtomicInteger off, char val) {
        return getString(b, off, (byte) val);
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个到结束符为value的String<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 结束符
     * @return 返回一个String
     */
    public String getString(byte[] b, AtomicInteger off, byte val) {
        int offset = off.get();
        int i = offset;
        int len = b.length;
        while (i < len) {//找到结束符直到最后
            if (b[i] == val) {break;}
            
            i++;
        }
        
        byte[] tmp = new byte[i - offset];
        System.arraycopy(b, offset, tmp, 0, i - offset);
        String str = new String(tmp, encoding);
        
        off.getAndSet(i + 1);
        return str;
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个定长的String，遇到\0结束<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param len 长度
     * @return 返回一个String
     */
    public String getString(byte[] b, AtomicInteger off, int len) {
        int offset = off.get();
        int i = 0;
        while (i < len) {
            if (offset + i >= b.length) {break;}
            if (b[offset + i] == 0) {
                break;//遇到\0结束
            }
            i++;
        }
        
        byte[] tmp = new byte[i];
        System.arraycopy(b, offset, tmp, 0, i);
        String str = new String(tmp, encoding);
        
        off.getAndAdd(len);
        return str;
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个定长的String，遇到\0结束<br>
     *
     * @param b      字节数组
     * @param off    偏移量
     * @param len    长度
     * @param hasEnd 有结束符
     * @return 返回一个String
     */
    public String getString(byte[] b, AtomicInteger off, int len, boolean hasEnd) {
        int offset = off.get();
        int i = 0;
        while (i < len) {
            if (offset + i >= b.length) {break;}
            if (b[offset + i] == 0) {
                break;//遇到\0结束
            }
            i++;
        }
        
        byte[] tmp = new byte[i];
        System.arraycopy(b, offset, tmp, 0, i);
        String str = new String(tmp, encoding);
        
        if (hasEnd) {off.getAndAdd(len + 1);}
        else {off.getAndAdd(len);}
        return str;
    }
    
    /**********************************************************/
    // 以下方法为在指定的偏移位,和值,插入到指定的字节数组的方法
    /**********************************************************/
    
    
    /**
     * 插入一个 <b>双字节</b> 的char<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 双字节char
     */
    public void putChar2(byte[] b, AtomicInteger off, char val) {
        off.getAndSet(putChar2(b, off.get(), val));
    }
    
    /**
     * 插入一个short<br>
     *
     * @param b      字节数组
     * @param offset 偏移量
     * @param val    short
     */
    public void putShort(byte[] b, AtomicInteger offset, short val) {
        offset.getAndSet(putShort(b, offset.get(), val));
    }
    
    /**
     * 插入一个int只保留两字节<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val int
     */
    public void putShort(byte[] b, AtomicInteger off, int val) {
        putShort(b, off, (short) val);
    }
    
    /**
     * 插入一个int<br>
     *
     * @param b      字节数组
     * @param offset 偏移量
     * @param val    int
     */
    public void putInt(byte[] b, AtomicInteger offset, int val) {
        offset.getAndSet(putInt(b, offset.get(), val));
    }
    
    /**
     * 插入一个float<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val float
     */
    public void putFloat(byte[] b, AtomicInteger off, float val) {
        off.getAndSet(putFloat(b, off.get(), val));
    }
    
    /**
     * 插入一个long<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val long
     */
    public void putLong(byte[] b, AtomicInteger off, long val) {
        off.getAndSet(putLong(b, off.get(), val));
    }
    
    /**
     * 插入一个double<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val double
     */
    public void putDouble(byte[] b, AtomicInteger off, double val) {
        off.getAndSet(putDouble(b, off.get(), val));
    }
    
    /**
     * 插入一个不定长的String,由str.getBytes().length来计算,无结束符<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param str 字节串
     */
    public void putString(byte[] b, AtomicInteger off, String str) {
        if (Validates.isEmpty(str)) {return;}
        
        byte[] ret = str.getBytes(encoding);
        System.arraycopy(ret, 0, b, off.get(), ret.length);
        off.getAndAdd(ret.length);
    }
    
    /**
     * 插入一个定长的String,由str.getBytes().length来计算,以endValue结束<br>
     *
     * @param b        字节数组
     * @param off      偏移量
     * @param str      字节串
     * @param endValue 结束符
     */
    public void putString(byte[] b, AtomicInteger off, String str, byte endValue) {
        if (Validates.isEmpty(str)) {//为空仅填充endValue
            b[off.getAndIncrement()] = endValue;
            return;
        }
        
        byte[] ret = str.getBytes(encoding);
        System.arraycopy(ret, 0, b, off.get(), ret.length);
        b[off.addAndGet(ret.length)] = endValue;
        off.getAndIncrement();
        
    }
    
    /**
     * 插入一个定长的String,无结束符<br>
     *
     * @param b      字节数组
     * @param offset 偏移量
     * @param str    字节串
     * @param len    长度
     */
    public void putString(byte[] b, AtomicInteger offset, String str, int len) {
        if (Validates.isEmpty(str)) {//为空偏移len
            offset.addAndGet(len);
            return;
        }
        
        byte[] ret = str.getBytes(encoding);
        
        if (ret.length > len) {System.arraycopy(ret, 0, b, offset.get(), len);}
        else {
            System.arraycopy(ret, 0, b, offset.get(), ret.length);
        }
        
        offset.addAndGet(len);
    }
    
    /**
     * 插入一个定长的String,并以一个byte结束<br>
     *
     * @param b        字节数组
     * @param offset   偏移量
     * @param str      字节串
     * @param len      长度
     * @param endValue 结束符
     */
    public void putString(byte[] b, AtomicInteger offset, String str, int len, byte endValue) {
        if (Validates.isEmpty(str)) {//为空偏移len再填endValue
            b[offset.addAndGet(len)] = endValue;
            offset.getAndIncrement();
            return;
        }
        
        byte[] ret = str.getBytes(encoding);
        
        if (ret.length > len) {System.arraycopy(ret, 0, b, offset.get(), len);}
        else {
            System.arraycopy(ret, 0, b, offset.get(), ret.length);
        }
        
        b[offset.addAndGet(len)] = endValue;
        offset.getAndIncrement();
    }
    
    /******************************************/
    // 以下方法为在给定的int型偏移位,给定的值,插入到指定的字节数组的方法
    
    /******************************************/
    
    public char getChar2(byte[] b, int off) {
        return isBigEndian ? (char) (((b[off + 1] & 0xFF) << 0) + ((b[off + 0] & 0xFF) << 8))
                           : (char) (((b[off + 0] & 0xFF) << 0) + ((b[off + 1] & 0xFF) << 8));
    }
    
    public short getShort(byte[] b, int off) {
        return (short) getShortUnsigned(b, off);
    }
    
    public int getShortUnsigned(byte[] b, int off) {
        return isBigEndian ? (((b[off + 1] & 0xFF) << 0) + ((b[off + 0] & 0xFF) << 8))
                           : (((b[off + 0] & 0xFF) << 0) + ((b[off + 1] & 0xFF) << 8));
    }
    
    public int getInt(byte[] b, int off) {
        return isBigEndian
               ? ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + ((b[off + 0] & 0xFF) << 24)
               : ((b[off + 0] & 0xFF) << 0) + ((b[off + 1] & 0xFF) << 8) + ((b[off + 2] & 0xFF) << 16) + ((b[off + 3] & 0xFF) << 24);
    }
    
    public long getIntUnsigned(byte[] b, int off) {
        return getInt(b, off) & 0xFFFFFFFFL;
    }
    
    public float getFloat(byte[] b, int off) {
        int i = isBigEndian
                ? ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + ((b[off + 0] & 0xFF) << 24)
                : ((b[off + 0] & 0xFF) << 0) + ((b[off + 1] & 0xFF) << 8) + ((b[off + 2] & 0xFF) << 16) + ((b[off + 3] & 0xFF) << 24);
        
        return Float.intBitsToFloat(i);
    }
    
    public long getLong(byte[] b, int off) {
        return isBigEndian
               ? ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + ((b[off + 4] & 0xFFL) << 24)
                       + ((b[off + 3] & 0xFFL) << 32) + ((b[off + 2] & 0xFFL) << 40) + ((b[off + 1] & 0xFFL) << 48) + ((b[off + 0] & 0xFFL) << 56)
               : ((b[off + 0] & 0xFFL) << 0) + ((b[off + 1] & 0xFFL) << 8) + ((b[off + 2] & 0xFFL) << 16) + ((b[off + 3] & 0xFFL) << 24)
                       + ((b[off + 4] & 0xFFL) << 32) + ((b[off + 5] & 0xFFL) << 40) + ((b[off + 6] & 0xFFL) << 48) + ((b[off + 7] & 0xFFL) << 56);
    }
    
    public double getDouble(byte[] b, int off) {
        long j = isBigEndian
                 ? ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + ((b[off + 4] & 0xFFL) << 24)
                         + ((b[off + 3] & 0xFFL) << 32) + ((b[off + 2] & 0xFFL) << 40) + ((b[off + 1] & 0xFFL) << 48) + ((b[off + 0] & 0xFFL) << 56)
                 : ((b[off + 0] & 0xFFL) << 0) + ((b[off + 1] & 0xFFL) << 8) + ((b[off + 2] & 0xFFL) << 16) + ((b[off + 3] & 0xFFL) << 24)
                         + ((b[off + 4] & 0xFFL) << 32) + ((b[off + 5] & 0xFFL) << 40) + ((b[off + 6] & 0xFFL) << 48) + ((b[off + 7] & 0xFFL) << 56);
        
        return Double.longBitsToDouble(j);
    }
    
    public String getString(byte[] b, int off, char val) {
        return getString(b, off, (byte) val);
    }
    
    public String getString(byte[] b, int off, byte val) {
        int i = off;
        int allLen = b.length;
        while (i < allLen) {
            if (b[i] == val) {break;}
            
            i++;
        }
        
        byte[] tmp = new byte[i - off];
        System.arraycopy(b, off, tmp, 0, i - off);
        
        return new String(tmp, encoding);
    }
    
    public String getString(byte[] b, int off, int len) {
        int i = 0;
        while (i < len) {
            if (off + i >= b.length) {break;}
            if (0 == b[off + i]) {break;}
            i++;
        }
        
        byte[] tmp = new byte[i];
        System.arraycopy(b, off, tmp, 0, i);
        
        return new String(tmp, encoding);
    }
    
    public int putChar2(byte[] b, int off, char val) {
        if (isBigEndian) {
            b[off + 1] = (byte) (val >>> 0);
            b[off + 0] = (byte) (val >>> 8);
        }
        else {
            b[off + 0] = (byte) (val >>> 0);
            b[off + 1] = (byte) (val >>> 8);
        }
        
        return off + 2;
    }
    
    public int putShort(byte[] b, int off, short val) {
        if (isBigEndian) {
            b[off + 1] = (byte) (val >>> 0);
            b[off + 0] = (byte) (val >>> 8);
        }
        else {
            b[off + 0] = (byte) (val >>> 0);
            b[off + 1] = (byte) (val >>> 8);
        }
        
        return off + 2;
    }
    
    public int putShort(byte[] b, int off, int val) {
        return putShort(b, off, (short) val);
    }
    
    public int putInt(byte[] b, int off, int val) {
        if (isBigEndian) {
            b[off + 3] = (byte) (val >>> 0);
            b[off + 2] = (byte) (val >>> 8);
            b[off + 1] = (byte) (val >>> 16);
            b[off + 0] = (byte) (val >>> 24);
        }
        else {
            b[off + 0] = (byte) (val >>> 0);
            b[off + 1] = (byte) (val >>> 8);
            b[off + 2] = (byte) (val >>> 16);
            b[off + 3] = (byte) (val >>> 24);
        }
        
        return off + 4;
    }
    
    public int putFloat(byte[] b, int off, float val) {
        int i = Float.floatToIntBits(val);
        if (isBigEndian) {
            b[off + 3] = (byte) (i >>> 0);
            b[off + 2] = (byte) (i >>> 8);
            b[off + 1] = (byte) (i >>> 16);
            b[off + 0] = (byte) (i >>> 24);
        }
        else {
            b[off + 0] = (byte) (i >>> 0);
            b[off + 1] = (byte) (i >>> 8);
            b[off + 2] = (byte) (i >>> 16);
            b[off + 3] = (byte) (i >>> 24);
        }
        
        return off + 4;
    }
    
    public int putLong(byte[] b, int off, long val) {
        if (isBigEndian) {
            b[off + 7] = (byte) (val >>> 0);
            b[off + 6] = (byte) (val >>> 8);
            b[off + 5] = (byte) (val >>> 16);
            b[off + 4] = (byte) (val >>> 24);
            b[off + 3] = (byte) (val >>> 32);
            b[off + 2] = (byte) (val >>> 40);
            b[off + 1] = (byte) (val >>> 48);
            b[off + 0] = (byte) (val >>> 56);
        }
        else {
            b[off + 0] = (byte) (val >>> 0);
            b[off + 1] = (byte) (val >>> 8);
            b[off + 2] = (byte) (val >>> 16);
            b[off + 3] = (byte) (val >>> 24);
            b[off + 4] = (byte) (val >>> 32);
            b[off + 5] = (byte) (val >>> 40);
            b[off + 6] = (byte) (val >>> 48);
            b[off + 7] = (byte) (val >>> 56);
        }
        
        return off + 8;
    }
    
    public int putDouble(byte[] b, int off, double val) {
        long j = Double.doubleToLongBits(val);
        return putLong(b, off, j);
    }
    
    public int putString(byte[] b, int off, String str) {
        if (Validates.isEmpty(str)) {return off;}
        
        byte[] ret = str.getBytes(encoding);
        System.arraycopy(ret, 0, b, off, ret.length);
        return off + ret.length;
    }
    
    public int putString(byte[] b, int off, String str, int len) {
        if (Validates.isEmpty(str)) {//为空仅偏移len
            return off + len;
        }
        
        byte[] ret = str.getBytes(encoding);
        
        if (ret.length > len) {System.arraycopy(ret, 0, b, off, len);}
        else {
            System.arraycopy(ret, 0, b, off, ret.length);
        }
        
        return off + len;
    }
    
    /**********************************************************/
    // 以下方法为String和byte[]之间的转换和长度获取
    /**********************************************************/
    
    /**
     * 获取编码后的字符串长度
     */
    public int getByteLen(String str) {
        if (str == null) {return 0;}
        
        try {
            return str.getBytes(encoding).length;
        }
        catch (Exception e) {
            return str.getBytes().length;
        }
    }
    
    public String toString(byte[] bytes) {
        if (bytes == null) {return null;}
        
        int len = 0;
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] == 0) {break;}
            
            len++;
        }
        
        return (len == 0) ? _EMPTY_ : new String(bytes, encoding);
    }
    
    public byte[] toBytes(short val) {
        byte[] b = new byte[2];
        putShort(b, 0, val);
        return b;
    }
    
    public byte[] toBytes(int val) {
        byte[] b = new byte[4];
        putInt(b, 0, val);
        return b;
    }
    
    public byte[] toBytes(long val) {
        byte[] b = new byte[8];
        putLong(b, 0, val);
        return b;
    }
    
    public byte[] toBytes(float val) {
        byte[] b = new byte[4];
        putFloat(b, 0, val);
        return b;
    }
    
    public byte[] toBytes(double val) {
        byte[] b = new byte[8];
        putDouble(b, 0, val);
        return b;
    }
    
    public byte[] toBytes(String str) {
        return (str == null) ? new byte[0] : str.getBytes(encoding);
    }
    
    public byte[] toBytes(String str, int len) {
        if (str == null) {return new byte[len];}
        
        byte[] buf = str.getBytes(encoding);
        byte[] bytes = new byte[len];
        if (buf.length > len) {System.arraycopy(buf, 0, bytes, 0, len);}
        else {
            System.arraycopy(buf, 0, bytes, 0, buf.length);
        }
        
        return bytes;
    }
    
    public byte[] toBytesByIp(String ip) {
        byte[] b = new byte[4];
        putIp4(b, 0, ip);
        return b;
    }
    
    /*******************************************************************/
    //转化成HEX字符串之后，再转化为byte[]
    /*******************************************************************/
    
    /**
     * 把每个字节转换成十六进制的字符串，再转换成byte[]
     */
    public static byte[] toHexBytes(byte[] b, LetterCase letter) {
        return toBytesASCII(Hexs.toHexString(b, letter));
    }
    
    /**
     * 把短整型转化为4个字节的HEX字符串的byte[]
     */
    public byte[] toHexBytes(short val, LetterCase letter) {
        return toHexBytes(toBytes(val), letter);
    }
    
    /**
     * 把整型转化为8个字节的HEX字符串的byte[]
     */
    public byte[] toHexBytes(int val, LetterCase letter) {
        return toHexBytes(toBytes(val), letter);
    }
    
    /**
     * 把整型转化为只保留4个字节的HEX字符串的byte[]
     */
    public byte[] toHexBytesByShort(int val, LetterCase letter) {
        return toHexBytes(toBytes((short) val), letter);
    }
    
    /**
     * 把短整型转化为4个字节的HEX字符串的byte[]
     */
    public void putHexBytes(byte[] b, int off, short val, LetterCase letter) {
        putBytes(b, off, toHexBytes(toBytes(val), letter));
    }
    
    /**
     * 把整型转化为8个字节的HEX字符串的byte[]
     */
    public void putHexBytes(byte[] b, int off, int val, LetterCase letter) {
        putBytes(b, off, toHexBytes(toBytes(val), letter));
    }
    
    /**
     * 把整型转化为只保留4个字节的HEX字符串的byte[]
     */
    public void putHexBytesByShort(byte[] b, int off, int val, LetterCase letter) {
        putBytes(b, off, toHexBytes(toBytes((short) val), letter));
    }
    
    /**********************************************************/
    // 以下方法静态方法byte[]处理方法
    /**********************************************************/
    
    /**
     * 字节数组转为字符数组，对byte进行0xFF操作
     *
     * @param bytes 字节数组
     * @return 字符数组
     */
    public static char[] toCharArray(byte[] bytes) {
        char[] result = new char[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            result[i] = (char) (bytes[i] & 0xFF);
        }
        return result;
    }
    
    /**
     * 字节数组转为字符数组，对byte进行0xFF操作
     *
     * @param bytes 字节数组
     * @param off   偏移量
     * @param len   长度
     * @return 字符数组
     */
    public static char[] toCharArray(byte[] bytes, int off, int len) {
        Asserts.as((bytes != null && bytes.length >= off + len) ? null : "字节数组转为字符数组时，长度不满足要求");
        
        char[] result = new char[len];
        for (int i = 0; i < len; i++, off++) {
            result[i] = (char) (bytes[off] & 0xFF);
        }
        return result;
    }
    
    /**
     * 字节数组无编码，直接转化为字符数组后转为字符串
     *
     * @param bytes 字节数组
     * @return 字符串
     */
    public static String toStringASCII(byte[] bytes) {
        return new String(toCharArray(bytes));
    }
    
    /**
     * 字节数组无编码，直接转化为字符数组后转为字符串
     *
     * @param bytes 字节数组
     * @param off   偏移量
     * @param len   长度
     * @return 字符串
     */
    public static String toStringASCII(byte[] bytes, int off, int len) {
        return new String(toCharArray(bytes, off, len));
    }
    
    /**
     * 指定字节缓冲和字符集生成字符串
     *
     * @param buf     字节缓冲
     * @param charset 字符集
     * @return 字符串
     * @throws CharacterCodingException 字符集异常
     */
    public static String toString(ByteBuffer buf, Charset charset) throws CharacterCodingException {
        CharsetDecoder decoder = charset.newDecoder();
        decoder.onMalformedInput(CodingErrorAction.REPORT);
        decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
        return decoder.decode(buf).toString();
    }
    
    /**
     * 获取指定编码的bytes，不抛UnsupportedEncodingException异常，规避多加异常代码
     */
    public static byte[] toBytes(String str, String encoding) {
        if (str == null) {return new byte[0];}
        
        try {
            return str.getBytes(encoding);
        }
        catch (UnsupportedEncodingException e) {
            throw Asserts.exception("不支持的编码格式[" + encoding + "]");
        }
    }
    
    /**
     * 获取[GBK],bytes，不抛UnsupportedEncodingException异常，规避多加异常代码
     */
    public static byte[] toBytesGBK(String str) {
        return (str == null) ? new byte[0] : str.getBytes(_GBK_C_);
    }
    
    /**
     * 获取[UTF-8]bytes，不抛UnsupportedEncodingException异常，规避多加异常代码
     */
    public static byte[] toBytesUTF8(String str) {
        return (str == null) ? new byte[0] : str.getBytes(_UTF_8_C_);
    }
    
    
    /**
     * 获取全ASCII字符串转化的bytes，无需编码
     */
    public static byte[] toBytesASCII(String str) {
        byte[] b = new byte[str.length()];
        for (int i = 0; i < b.length; i++) {
            char c = str.charAt(i);
            Asserts.as(c <= 127 ? null : "存在非ASCII字符，不支持");
            
            b[i] = (byte) c;
        }
        return b;
    }
    
    /**
     * 获取long转化为字符串后转化的bytes
     */
    public static byte[] toBytesASCII(long val) {
        return toBytesASCII(String.valueOf(val));
    }
    
    /**
     * 获取int转化为字符串后转化的bytes
     */
    public static byte[] toBytesASCII(int val) {
        return toBytesASCII(String.valueOf(val));
    }
    
    /**
     * 填充指定字节，到byte[]的指定的起始位置和长度中去
     *
     * @param bytes 字节数组
     * @param off   起始位置
     * @param b     字节
     * @param len   长度
     */
    public static void fillBytes(byte[] bytes, int off, byte b, int len) {
        for (int i = 0; i < bytes.length; i++) {
            if (i < off) {continue;}
            
            if (i == (off + len)) {break;}
            
            bytes[i] = b;
        }
    }
    
    /**
     * 获取指定编码的byte长度
     */
    public static int getByteLen(String str, String encoding) {
        return toBytes(str, encoding).length;
    }
    
    /**
     * 获取源数组从起始位置后面的字节数组
     *
     * @param b      源字节数组
     * @param offset 源字节数组起始位置
     * @return 新的字节数组
     */
    public static byte[] getBytes(byte[] b, int offset) {
        byte[] bytes = new byte[b.length - offset];
        System.arraycopy(b, offset, bytes, 0, b.length - offset);
        return bytes;
    }
    
    /**
     * 获取指定长度的字节数组，从off计算，如果源数组不足，后补0，不补0方法请使用getBytesAvailable
     *
     * @param b   源字节数组
     * @param off 源字节数组起始位置
     * @param len 指定的长度
     * @return 新的字节数组
     */
    public static byte[] getBytes(byte[] b, int off, int len) {
        byte[] bytes = new byte[len];
        if (len + off <= b.length) {System.arraycopy(b, off, bytes, 0, len);}
        else {
            System.arraycopy(b, off, bytes, 0, b.length - off);
        }
        
        return bytes;
    }
    
    /**
     * 获取指定结尾字符的字节数组
     *
     * @param b   源字节数组
     * @param off 源字节数组起始位置
     * @param val 指定的配对字符
     * @return 获取到的字节数组
     */
    public static byte[] getBytes(byte[] b, int off, char val) {
        return getBytes(b, off, (byte) val);
    }
    
    /**
     * 获取指定结尾字符的字节数组
     *
     * @param b   源字节数组
     * @param off 源字节数组起始位置
     * @param val 指定的配对字节
     * @return 获取到的字节数组
     */
    public static byte[] getBytes(byte[] b, int off, byte val) {
        int len = next(b, off, val) - off - 1;
        return getBytes(b, off, len);
    }
    
    /**
     * 获取指定结尾字符的字节数组
     *
     * @param b   源字节数组
     * @param off 源字节数组起始位置
     * @param val 指定的配对字符
     * @return 获取到的字节数组
     */
    public static byte[] getBytes(byte[] b, AtomicInteger off, char val) {
        return getBytes(b, off, (byte) val);
    }
    
    /**
     * 获取指定结尾字节的字节数组
     *
     * @param b   源字节数组
     * @param off 源字节数组起始位置
     * @param val 指定的配对字节
     * @return 获取到的字节数组
     */
    public static byte[] getBytes(byte[] b, AtomicInteger off, byte val) {
        int offset = off.get();
        off.getAndSet(next(b, offset, val));
        return getBytes(b, offset, off.get() - offset - 1);
    }
    
    /**
     * 获取可用的字节数组，定长请使用getBytes，从off计算，如果如果源数组长度大于等于len则取len，少于len，则取实际b.length-
     * off长度
     *
     * @param b   源字节数组
     * @param off 起始位置
     * @param len 要求的长度
     * @return byte[] 可用的字节数组
     */
    public static byte[] getBytesAvailable(byte[] b, int off, int len) {
        if (len + off <= b.length) {
            byte[] bytes = new byte[len];
            System.arraycopy(b, off, bytes, 0, len);
            return bytes;
        }
        else {
            byte[] bytes = new byte[b.length - off];
            System.arraycopy(b, off, bytes, 0, b.length - off);
            return bytes;
        }
    }
    
    /**
     * 判断字节数组是否有前缀
     *
     * @param b      字节数组
     * @param prefix 字节前缀
     * @return ＝true表示有前缀
     */
    public static boolean isPrefix(byte[] b, byte[] prefix) {
        return match(b, 0, prefix) != -1;
    }
    
    /**
     * 判断字节数组是否从偏移位匹配到指定字节数组
     *
     * @param b     字节数组
     * @param off   字节数组偏移位
     * @param match 匹配的字节数组
     * @return =-1表示未匹配成功，
     * !=-1表示匹配成功，off向后偏移match.length
     */
    public static int match(byte[] b, int off, byte[] match) {
        Asserts.nonNull(match, "match");
        
        if (b == null || b.length < match.length + off) {return -1;}
        
        for (int i = 0; i < match.length; i++, off++) {
            if (b[off] != match[i]) {return -1;}
        }
        
        return off;
    }
    
    /**********************************************************/
    // 以下方法为判断是否图片格式，当前支持jpg,gif,png
    /**********************************************************/
    
    /**
     * 判断是否PNG 格式，要求长度大于8，前4字节为0x89 0x50 0x4E 0x47
     *
     * @param b 字节数组
     * @return =true表示PNG
     */
    public static boolean isPNG(byte[] b) {
        return b != null && b.length >= 8 && isPrefix(b, _PNG_HDR_);
    }
    
    /**
     * 判断是否JPG格式，要求长度大于4，前2字节为0xFF 0xD8
     *
     * @param b 字节数组
     * @return =true表示JPG
     */
    public static boolean isJPG(byte[] b) {
        return b != null && b.length >= 4 && isPrefix(b, _JPG_HDR_);
    }
    
    /**
     * 判断是否GIF格式，要求长度大于6，前3字节为0x47 0x49 0x46
     *
     * @param b 字节数组
     * @return =true表示GIF
     */
    public static boolean isGIF(byte[] b) {
        return b != null && b.length >= 6 && isPrefix(b, _GIF_HDR_);
    }
    
    /**********************************************************/
    // 以下方法为判断是否BOM
    /**********************************************************/
    
    /**
     * 是否是UTF8格式//EF BB BF
     */
    public static boolean isUTF8BOM(byte[] b) {
        return isPrefix(b, _BOM_UTF8_);
    }
    
    /**
     * 是否是UTF16BE格式//FE FF
     */
    public static boolean isUTF16BEBOM(byte[] b) {
        return isPrefix(b, _BOM_UTF16BE_);
    }
    
    /**
     * 是否是UTF16LE格式//FF FE
     */
    public static boolean isUTF16LEBOM(byte[] b) {
        return isPrefix(b, _BOM_UTF16LE_);
    }
    
    /**
     * 是否是UTF32BE格式//FF FE 00 00
     */
    public static boolean isUTF32LEBOM(byte[] b) {
        return isPrefix(b, _BOM_UTF32LE_);
    }
    
    /**
     * 是否是UTF16BE格式 00 00 FE FF
     */
    public static boolean isUTF32BEBOM(byte[] b) {
        return isPrefix(b, _BOM_UTF32BE_);
    }
    
    
    /**********************************************************/
    // 在byte[]中向前向后寻找下一个字符位置
    /**********************************************************/
    
    /**
     * 向后寻找指定字符的位置，没找到off移动到最后
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 字符
     * @return 找到字符的偏移量
     */
    public static final int next(byte[] b, int off, char val) {
        return next(b, off, (byte) val);
    }
    
    /**
     * 向后寻找指定字节的位置，没找到off移动到最后
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 字节
     * @return 找到字符的偏移量
     */
    public static final int next(byte[] b, int off, byte val) {
        int len = b.length;
        while (off < len) {
            if (b[off++] == val) {return off;}
        }
        return off;
    }
    
    /**
     * 向后寻找换行符的位置，没找到off移动到最后
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 找到换行符的偏移量
     */
    public static final int nextLF(byte[] b, int off) {
        return next(b, off, '\n');
    }
    
    /**
     * 向后寻找换行符和指定字符之一的位置，没找到off移动到最后
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 字符
     * @return 找到换行符或指定字符的偏移量
     */
    public static final int nextLF(byte[] b, int off, char val) {
        int len = b.length;
        while (off < len) {
            byte c = b[off++];
            if (c == val || c == '\n') {return off;}
        }
        return off;
    }
    
    /**
     * 向前寻找指定字符位置，没找到off移动到-1
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 字符
     * @return 找到指定字符的偏移量
     */
    public static final int prev(byte[] b, int off, final char val) {
        if (off >= b.length) {off = b.length - 1;}
        
        while (off >= 0) {
            if (b[off--] == val) {return off;}
        }
        return off;
    }
    
    /**
     * 向前寻找换行符的位置，没找到off移动到-1
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 找到指定字符的偏移量
     */
    public static final int prevLF(byte[] b, int off) {
        return prev(b, off, '\n');
    }
    
    /**
     * 向前寻找换行符和指定字符之一的位置，没找到off移动到-1
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 字符
     * @return 找到换行符或指定字符的偏移量
     */
    public static final int prevLF(byte[] b, int off, final char val) {
        if (off >= b.length) {off = b.length - 1;}
        
        while (off >= 0) {
            byte c = b[off--];
            if (c == val || c == '\n') {return off;}
        }
        return off;
    }
    
    /***************************************************************************************************/
    // 静态方法写入字节&字节数组
    
    /***************************************************************************************************/
    
    public static int putBOMUTF8(byte[] b, int off) {
        b[off + 0] = (byte) 0xEF;
        b[off + 1] = (byte) 0xBB;
        b[off + 2] = (byte) 0xBF;
        
        return off + 3;
    }
    
    public static int putBOMUTF16LE(byte[] b, int off) {
        b[off + 0] = (byte) 0xFF;
        b[off + 1] = (byte) 0xFE;
        
        return off + 2;
    }
    
    public static int putBOMUTF16BE(byte[] b, int off) {
        b[off + 0] = (byte) 0xFE;
        b[off + 1] = (byte) 0xFF;
        
        return off + 2;
    }
    
    public static int putBOMUTF32LE(byte[] b, int off) {
        b[off + 0] = (byte) 0xFF;
        b[off + 1] = (byte) 0xFE;
        b[off + 2] = (byte) 0x00;
        b[off + 3] = (byte) 0x00;
        
        return off + 4;
    }
    
    public static int putBOMUTF32BE(byte[] b, int off) {
        b[off + 0] = (byte) 0x00;
        b[off + 1] = (byte) 0x00;
        b[off + 2] = (byte) 0xFE;
        b[off + 3] = (byte) 0xFF;
        
        return off + 4;
    }
    
    public static int putByte(byte[] b, int off, byte val) {
        b[off] = val;
        
        return off + 1;
    }
    
    public static int putByte(byte[] b, int off, int val) {
        b[off] = (byte) val;
        return off + 1;
    }
    
    public static int putBytes(byte[] b, int off, byte[] src) {
        if (src == null || src.length == 0) {return off;}
        
        System.arraycopy(src, 0, b, off, src.length);
        return off + src.length;
    }
    
    public static int putBytes(byte[] b, int off, byte[] src, int len) {
        if (src == null || src.length == 0) {//为空仅偏移
            return off + len;
        }
        
        System.arraycopy(src, 0, b, off, len);
        return off + len;
    }
    
    public static int putBoolean(byte[] b, int off, boolean val) {
        b[off] = (byte) (val ? 1 : 0);
        return off + 1;
    }
    
    public static int putChar1(byte[] b, int off, char val) {
        b[off + 0] = (byte) (val & 0xFF);
        
        return off + 1;
    }
    
    /**
     * 插入UTF-8的BOM（三个字节EF BB BF）
     *
     * @param b   字节数组
     * @param off 偏移量
     */
    public static void putBOMUTF8(byte[] b, AtomicInteger off) {
        off.getAndSet(putBOMUTF8(b, off.get()));
    }
    
    /**
     * 插入UTF16 LE的BOM（两个字节FF FE）
     *
     * @param b   字节数组
     * @param off 偏移量
     */
    public static void putBOMUTF16LE(byte[] b, AtomicInteger off) {
        off.getAndSet(putBOMUTF16LE(b, off.get()));
    }
    
    /**
     * 插入UTF16 BE的BOM（两个字节FE FF）
     *
     * @param b   字节数组
     * @param off 偏移量
     */
    public static void putBOMUTF16BE(byte[] b, AtomicInteger off) {
        off.getAndSet(putBOMUTF16BE(b, off.get()));
    }
    
    /**
     * 插入UTF32 LE的BOM（四个字节FF FE 00 00）
     *
     * @param b      字节数组
     * @param offset 偏移量
     */
    public static void putBOMUTF32LE(byte[] b, AtomicInteger offset) {
        offset.getAndSet(putBOMUTF32LE(b, offset.get()));
    }
    
    /**
     * 插入UTF32 BE的BOM（四个字节00 00 FE FF）
     *
     * @param b   字节数组
     * @param off 偏移量
     */
    public static void putBOMUTF32BE(byte[] b, AtomicInteger off) {
        off.getAndSet(putBOMUTF32BE(b, off.get()));
    }
    
    /**
     * 插入一个不定长字节数组<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param src 要插入的字符数组
     */
    public static void putBytes(byte[] b, AtomicInteger off, byte[] src) {
        if (src == null || src.length == 0) {return;}
        
        System.arraycopy(src, 0, b, off.get(), src.length);
        off.addAndGet(src.length);
    }
    
    /**
     * 插入一个定长字节数组，如果src为null或为空取new byte[len]<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param src 要插入的字符数组
     * @param len 长度
     */
    public static void putBytes(byte[] b, AtomicInteger off, byte[] src, int len) {
        if (len <= 0) {return;}
        
        int offset = off.get();
        if (src == null || src.length == 0) {//为空仅偏移
            offset += len;
            return;
        }
        
        System.arraycopy(src, 0, b, offset, len);
        off.addAndGet(len);
    }
    
    /**
     * 插入一个字节
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val byte
     */
    public static void putByte(byte[] b, AtomicInteger off, byte val) {
        b[off.getAndIncrement()] = val;
    }
    
    /**
     * 插入一个字节
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val int转为byte
     */
    public static void putByte(byte[] b, AtomicInteger off, int val) {
        b[off.getAndIncrement()] = (byte) val;
    }
    
    /**
     * 插入一个boolean<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val booelan
     */
    public static void putBoolean(byte[] b, AtomicInteger off, boolean val) {
        b[off.getAndIncrement()] = (byte) (val ? 1 : 0);
    }
    
    /**
     * 插入一个 <b>单字节</b> 的char<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val 单字节char
     */
    public static void putChar1(byte[] b, AtomicInteger off, char val) {
        b[off.getAndIncrement()] = (byte) val;
    }
    
    public static int putIp4(byte[] b, int off, String value) {
        if (value == null) {value = "0.0.0.0";}
        
        String[] strs = value.split("\\.");
        if (strs == null || strs.length != 4) {return off;}
        
        for (int i = 0; i < strs.length; i++) {
            b[off + i] = (byte) Integer.parseInt(strs[i]);
        }
        
        return off + 4;
    }
    
    public static int putMac(byte[] b, int off, String value) {
        if (value == null) {value = "00:00:00:00:00:00:00";}
        byte[] val = Hexs.toBytes(value, ":");
        System.arraycopy(val, 0, b, off + 0, val.length);
        return off + 6;
    }
    
    /**
     * 插入一个Ip4<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val int
     */
    public static void putIp4(byte[] b, AtomicInteger off, String val) {
        off.addAndGet(putIp4(b, off.get(), val));
    }
    
    /**
     * 插入一个Mac<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param val int
     */
    public static void putMac(byte[] b, AtomicInteger off, String val) {
        off.addAndGet(putMac(b, off.get(), val));
    }
    
    /***************************************************************************************************/
    // 静态方法获取字节&字节数组
    
    /***************************************************************************************************/
    
    public static byte getByte(byte[] b, int off) {
        return b[off];
    }
    
    public static int getByteUnsigned(byte[] b, int off) {
        return b[off] & 0xFF;
    }
    
    public static boolean getBoolean(byte[] b, int off) {
        return b[off] != 0;
    }
    
    public static char getChar1(byte[] b, int off) {
        return (char) ((b[off + 0] & 0xFF));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个byte<br>
     *
     * @param b   字节数组
     * @param off 可变偏移量
     * @return byte     得到byte
     */
    public static byte getByte(byte[] b, AtomicInteger off) {
        return b[off.getAndIncrement()];
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个byte<br>
     *
     * @param b   字节数组
     * @param off 可变偏移量
     * @return int      得到byte的无符号整型(0-255)
     */
    public static int getByteUnsigned(byte[] b, AtomicInteger off) {
        return b[off.getAndIncrement()] & 0xFF;
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个boolean<br>
     *
     * @param b   字节数组
     * @param off 可变偏移量
     * @return boolean  得到boolean
     */
    public static boolean getBoolean(byte[] b, AtomicInteger off) {
        return b[off.getAndIncrement()] != 0;
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个 <b>单字节</b> 的char<br>
     *
     * @param b   字节数组
     * @param off 可变偏移量
     * @return char     得到单字节的char
     */
    public static char getChar1(byte[] b, AtomicInteger off) {
        return getChar1(b, off.getAndIncrement());
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个定长为len的byte[]<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @param len 读起的长度
     * @return 返回一个byte[]
     */
    public static byte[] getBytes(byte[] b, AtomicInteger off, int len) {
        int offset = off.get();
        
        byte[] bytes = new byte[len];
        if (len + offset <= b.length) {System.arraycopy(b, offset, bytes, 0, len);}
        else {
            System.arraycopy(b, offset, bytes, 0, b.length - offset);
        }
        
        off.addAndGet(len);
        return bytes;
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个到b末端的byte[]<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个byte[]
     */
    public static byte[] getBytes(byte[] b, AtomicInteger off) {
        int offset = off.get();
        byte[] bytes = getBytes(b, offset);
        off.addAndGet(bytes.length);
        return bytes;
    }
    
    
    public static String getIp4(byte[] b, int off) {
        StringBuilder strb = new StringBuilder();
        
        strb.append(b[off + 0] & 0xFF).append(".");
        strb.append(b[off + 1] & 0xFF).append(".");
        strb.append(b[off + 2] & 0xFF).append(".");
        strb.append(b[off + 3] & 0xFF);
        
        return strb.toString();
    }
    
    public static String getMac(byte[] b, int off) {
        StringBuilder strb = new StringBuilder();
        
        for (int i = 0; i < 6; i++) {
            if (i != 0) {strb.append(":");}
            
            String str = Integer.toString((b[off + i] & 0xff) + 0x100, 16).substring(1);
            strb.append(str);
        }
        
        return strb.toString();
    }
    
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个v4的IP地址<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个192.168.0.1的IP地址
     */
    public static String getIp4(byte[] b, AtomicInteger off) {
        return getIp4(b, off.addAndGet(4));
    }
    
    /**
     * 从字节数组b中 根据偏移量offset读起一个Mac地址<br>
     *
     * @param b   字节数组
     * @param off 偏移量
     * @return 返回一个EA:12:DE:35:FE:87的MAC地址
     */
    public static String getMac(byte[] b, AtomicInteger off) {
        return getMac(b, off.addAndGet(6));
    }
}
